
package com.xjoyt.pay.master.services;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import com.xjoyt.pay.abstracts.AbstractService;
import com.xjoyt.pay.annotations.LockMethod;
import com.xjoyt.pay.annotations.LockParam;
import com.xjoyt.pay.commons.ObjectUtils;
import com.xjoyt.pay.commons.PayTypeUtils;
import com.xjoyt.pay.dto.ExecuteResult;
import com.xjoyt.pay.dto.PageParam;
import com.xjoyt.pay.dto.PageResult;
import com.xjoyt.pay.gateway.params.AlipayOrderParam;
import com.xjoyt.pay.gateway.params.AlipayWapPayParam;
import com.xjoyt.pay.gateway.params.BankPayParam;
import com.xjoyt.pay.gateway.params.CallbackParam;
import com.xjoyt.pay.gateway.params.ScannedCodeParam;
import com.xjoyt.pay.gateway.params.WechatOrderParam;
import com.xjoyt.pay.gateway.params.WechatWapPayParam;
import com.xjoyt.pay.master.core.order.OrderAlipayScanCreateHandle;
import com.xjoyt.pay.master.core.order.OrderAlipayWapPayCreateHandle;
import com.xjoyt.pay.master.core.order.OrderBankPayCreateHandle;
import com.xjoyt.pay.master.core.order.OrderCallbackHandle;
import com.xjoyt.pay.master.core.order.OrderWechatScanCreateHandle;
import com.xjoyt.pay.master.core.order.OrderWechatWapPayCreateHandle;
import com.xjoyt.pay.master.core.order.ScannedCodePayCreateHandle;
import com.xjoyt.pay.master.dto.CallbackResult;
import com.xjoyt.pay.master.dto.OrderUrlResult;
import com.xjoyt.pay.master.params.MerchantOrderGroupParam;
import com.xjoyt.pay.master.params.MerchantPageParam;
import com.xjoyt.pay.master.params.OrderPageParam;
import com.xjoyt.pay.master.po.Merchant;
import com.xjoyt.pay.master.po.Order;
import com.xjoyt.pay.master.repository.MerchantRepository;
import com.xjoyt.pay.master.repository.OrderRepository;
import com.xjoyt.pay.master.vo.MerchantOrderGroup;
import com.xjoyt.pay.master.vo.OrderMap;
import com.xjoyt.pay.master.vo.OrderParam;
import com.xjoyt.pay.vo.PayType;

@Service
public class OrderService extends AbstractService {

	@Autowired
	private OrderRepository repository;

	@Autowired
	private MerchantRepository merchantRepository;

	@Autowired
	private OrderCallbackHandle orderCallbackHandle;

	@Autowired
	private OrderWechatScanCreateHandle wechatScanHandle;

	@Autowired
	private OrderBankPayCreateHandle bankPayHandle;

	@Autowired
	private OrderAlipayWapPayCreateHandle alipayWapPayHandle;

	@Autowired
	private OrderWechatWapPayCreateHandle wechatWapPayHandle;

	@Autowired
	private ScannedCodePayCreateHandle scannedCodePayHandle;

	@Autowired
	private OrderAlipayScanCreateHandle alipayScanHandle;

	public Order get(String id) {
		return repository.findOne(id);
	}

	public Order getByNo(String no) {
		return repository.findOneByNo(no);
	}

	public Order getByCustomerNo(String customerNo) {
		return repository.findOneByOutTradeNo(customerNo);
	}

	public PageResult<Order> loadPage(PageParam<OrderPageParam> param) {
		return repository.loadPage(param);
	}

	public PageResult<OrderMap> loadPageMap(PageParam<OrderPageParam> param) {
		PageResult<Order> page = repository.loadPage(param);
		List<OrderMap> list = new ArrayList<>();
		List<String> ids = new ArrayList<>();
		for (Order item : page) {
			ids.add(item.id);
			OrderMap vo = new OrderMap();
			list.add(vo);
			ObjectUtils.fieldCopy(item, vo);
		}
		List<Object> footer = new ArrayList<>();
		if (CollectionUtils.isNotEmpty(ids) && param.query != null) {
			list.forEach(item -> {
				for (PayType type : PayType.values()) {
					if (item.payType == type) {
						item.payTypeName = PayTypeUtils.getName(type);
					}
				}
			});

			Map<String, Object> sumList = repository.sum(param.query);
			if (sumList != null && sumList.get("sum") != null && sumList.get("count") != null) {
				Map<String, Double> map = new HashMap<>();
				map.put("moneySumOfAll", Double.valueOf(sumList.get("sum").toString()));
				map.put("orderSum", Double.valueOf(sumList.get("count").toString()));
				footer.add(map);
			}

		}

		return new PageResult<>(page.getTotal(), list, footer);
	}

	/**
	 * 回调处理
	 * 
	 * @param p
	 * @return
	 * @throws Exception
	 */
	@LockMethod
	public ExecuteResult<CallbackResult> callback(@LockParam("id") CallbackParam param) throws Exception {
		return orderCallbackHandle.handle(param);
	}

	@LockMethod
	public ExecuteResult<OrderUrlResult> wechatScan(@LockParam({ "merchantId", "outTradeNo" }) WechatOrderParam param) {
		PayType payType = PayType.WECHAT_SCAN;
		return wechatScanHandle.handle(param, payType);
	}

	@LockMethod
	public ExecuteResult<OrderUrlResult> alipayScan(@LockParam({ "merchantId", "outTradeNo" }) AlipayOrderParam param) {
		PayType payType = PayType.ALIPAY_SCAN;
		return alipayScanHandle.handle(param, payType);
	}

	@LockMethod
	public ExecuteResult<OrderUrlResult> bankPay(@LockParam({ "merchantId", "outTradeNo" }) BankPayParam param) {
		PayType payType = PayType.BANK_PAY;
		return bankPayHandle.handle(param, payType);
	}

	@LockMethod
	public ExecuteResult<OrderUrlResult> wechatWapPay(
			@LockParam({ "merchantId", "outTradeNo" }) WechatWapPayParam param) {
		PayType payType = PayType.WECHAT_WAP;
		return wechatWapPayHandle.handle(param, payType);
	}

	@LockMethod
	public ExecuteResult<OrderUrlResult> alipayWapPay(
			@LockParam({ "merchantId", "outTradeNo" }) AlipayWapPayParam param) {
		PayType payType = PayType.ALIPAY_WAP;
		return alipayWapPayHandle.handle(param, payType);
	}

	@LockMethod
	public ExecuteResult<OrderUrlResult> scannedCodePay(
			@LockParam({ "merchantId", "outTradeNo" }) ScannedCodeParam param) {
		PayType payType = PayType.SCANNED_CODE_PAY;
		if (StringUtils.isBlank(param.authCode))
			return ExecuteResult.fail("授权码为空");

		if (param.payMethod == null || param.payMethod.equals(" "))
			return ExecuteResult.fail("支付方式为空");
         
		return scannedCodePayHandle.handle(param, payType);
	}

	public ExecuteResult<Order> checkout(OrderParam param) {
		if (StringUtils.isBlank(param.id)) {
			return ExecuteResult.fail("参数不能为空");
		}
		Order entity = repository.findOne(param.id);
		if (entity == null) {
			return ExecuteResult.fail("该订单不存在");
		}
		if (BooleanUtils.isTrue(entity.check)) {
			return ExecuteResult.fail("此订单已经结算");
		}
		entity.payDate = new Date();
		entity.pay = true;
		repository.save(entity);
		return ExecuteResult.ok(entity);
	}

	public PageResult<MerchantOrderGroup> loadPageGroup(@RequestBody PageParam<MerchantOrderGroupParam> param) {

		PageParam<MerchantPageParam> p = new PageParam<>();
		ObjectUtils.fieldCopy(param, p);
		PageResult<Merchant> page = merchantRepository.loadPage(p);

		List<String> merchantIds = new ArrayList<>();
		List<MerchantOrderGroup> list = new ArrayList<>();
		for (Merchant item : page) {
			merchantIds.add(item.id);
			MerchantOrderGroup vo = new MerchantOrderGroup();
			list.add(vo);
			ObjectUtils.fieldCopy(item, vo);
		}

		List<Object> footer = new ArrayList<>();

		if (CollectionUtils.isNotEmpty(merchantIds) && param.query != null) {

			List<Map<String, Object>> sumList = repository.merchantSum(merchantIds, param.query);

			double moneySumOfAll = 0;
			for (Map<String, Object> map : sumList) {
				if (map == null || !map.containsKey("id") || map.get("id") == null)
					continue;
				Object merchantId = map.get("id");
				for (MerchantOrderGroup item : list) {
					if (merchantId.equals(item.id)) {
						item.moneySum = (map.containsKey("sum") && map.get("sum") != null)
								? Double.valueOf(map.get("sum").toString()) : 0D;
						item.numberOfOrders = (map.containsKey("count") && map.get("count") != null)
								? (long) map.get("count") : 0L;
						moneySumOfAll += item.moneySum;
						break;
					}
				}
			}

			Map<String, Object> map = new HashMap<>();
			map.put("moneySumOfAll", moneySumOfAll);
			footer.add(map);

		}

		return new PageResult<>(page.getTotal(), list, footer);
	}

	public List<Order> findAll(OrderPageParam params) {
		// TODO Auto-generated method stub
		return repository.findAll(params);
	}
}
